package puntaelena.facilitylocation.hillclimbing.experiment.observeneighbors;

import java.util.List;
import java.util.ArrayList;

import laboratoryQ.experiment.IExperiment;
import laboratoryQ.experiment.IExperimentResult;
import laboratoryQ.experiment.process.IExperimentContext;
import puntaelena.graphsupport.solver.IFacilityLocationSolver;
import puntaelena.graphsupport.FacilitiesGraph;
import puntaelena.graphsupport.GenerateGrSettings;
import puntaelena.graphsupport.GraphSupportFront;

public class PuntaElenaExperiment implements IExperiment { // TODO: rename 2 neighbor selection experiment
	public IExperimentResult runExperiment(IExperimentContext icontext, IExpRunStatusHandler expRunStatusHandler) {
		PuntaElenaExperimentContext context = (PuntaElenaExperimentContext) icontext;
		PuntaElenaExperimentSettings settings = context.getExperimentSettings();
		
		int totalCount = 0;
		totalCount += compareBinTrivialGETItemsCount(settings);
		totalCount += compareFactor3TrivialBigGETItemsCount(settings);
		int[] counter_ptr = new int[1];

		context.compareBinTrivialSmallTestCosts = compareBinTrivial(expRunStatusHandler, context, totalCount, counter_ptr);
		context.compareFactor3TrivialBigTestCosts = compareFactor3TrivialBig(expRunStatusHandler, context, totalCount, counter_ptr);

		expRunStatusHandler.handleExperimentResult(context);
		return context;
	}

	private int compareFactor3TrivialBigGETItemsCount(PuntaElenaExperimentSettings settings) {
		return settings.neighborCountEndBig - settings.neighborCountBegBig + 1;
	}

	private List<double[]> compareFactor3TrivialBig(
		IExpRunStatusHandler expRunStatusHandler,
		PuntaElenaExperimentContext context,
		int experimentLength,
		int[] counterPtr
	) {
		PuntaElenaExperimentSettings settings = context.getExperimentSettings();
		int solversCount = settings.testingSolvers.length;
		List<double[]> result = new ArrayList<double[]>();
		GenerateGrSettings gsett = new GenerateGrSettings(settings.citiesCountBig, settings.facilitiesCountBig);
		gsett.randomSeed = context.getRandom().get() * 1000000;

		for (int i = 0; i < solversCount; i++) {
			result.add(new double[settings.neighborCountEndBig - settings.neighborCountBegBig + 1]);
		}
		IFacilityLocationSolver trivialSolver = GraphSupportFront.getTrivialSolver();
		IFacilityLocationSolver factor3Solver = GraphSupportFront.getFactor3Solver();

		for (int neighborsCount = settings.neighborCountBegBig; neighborsCount <= settings.neighborCountEndBig; neighborsCount++) {
			gsett.neighborCountBeg = neighborsCount;
			gsett.neighborCountRange = 1;
			gsett.cheapEdgeBeg = settings.cheapEdgeBeg;
			gsett.cheapEdgeRange = settings.cheapEdgeRange;
			gsett.expensiveEdgeBeg = settings.expensiveEdgeBeg;
			gsett.expensiveEdgeRange = settings.expensiveEdgeRange;
			gsett.facilityPriceBeg = settings.facilityPriceBeg;
			gsett.facilityPriceRange = settings.facilityPriceRange;
			FacilitiesGraph fgraph = (FacilitiesGraph) settings.graphGenerator.generate(gsett);

			boolean[] isfcltOpen = new boolean[fgraph.getFacilitiesCount()];
			int[] witnesses = new int[fgraph.getCitiesCount()];
			
			trivialSolver.solve(fgraph, isfcltOpen, witnesses);
			double cost = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
			result.get(0)[neighborsCount - settings.neighborCountBegBig] = cost;
			
			factor3Solver.solve(fgraph, isfcltOpen, witnesses);
			cost = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
			result.get(1)[neighborsCount - settings.neighborCountBegBig] = cost;

			expRunStatusHandler.stepsProcessed(
					counterPtr[0],
					experimentLength
			);
			counterPtr[0]++;
		}
		return result;
	}
	
	private int compareBinTrivialGETItemsCount(PuntaElenaExperimentSettings settings) {
		return settings.neighborCountEnd - settings.neighborCountBeg + 1;
	}

	private List<double[]> compareBinTrivial(
		IExpRunStatusHandler expRunStatusHandler,
		PuntaElenaExperimentContext context,
		int experimentLength,
		int[] counterPtr
	) {
		PuntaElenaExperimentSettings settings = context.getExperimentSettings();
		int solversCount = settings.testingSolvers.length;
		List<double[]> result = new ArrayList<double[]>();
		GenerateGrSettings gsett = new GenerateGrSettings(settings.citiesCount, settings.facilitiesCount);
		gsett.randomSeed = context.getRandom().get() * 1000000;

		for (int i = 0; i < solversCount; i++) {
			result.add(new double[settings.neighborCountEnd - settings.neighborCountBeg + 1]);
		}

		for (int neighborsCount = settings.neighborCountBeg; neighborsCount <= settings.neighborCountEnd; neighborsCount++) {
			gsett.neighborCountBeg = neighborsCount;
			gsett.neighborCountRange = 1;
			gsett.cheapEdgeBeg = settings.cheapEdgeBeg;
			gsett.cheapEdgeRange = settings.cheapEdgeRange;
			gsett.expensiveEdgeBeg = settings.expensiveEdgeBeg;
			gsett.expensiveEdgeRange = settings.expensiveEdgeRange;
			gsett.facilityPriceBeg = settings.facilityPriceBeg;
			gsett.facilityPriceRange = settings.facilityPriceRange;
			FacilitiesGraph fgraph = (FacilitiesGraph) settings.graphGenerator.generate(gsett);

			for (int i = 0; i < settings.testingSolvers.length; i++) {
				boolean[] isfcltOpen = new boolean[fgraph.getFacilitiesCount()];
				int[] witnesses = new int[fgraph.getCitiesCount()];
				settings.testingSolvers[i].solve(fgraph, isfcltOpen, witnesses);
				double cost = GraphSupportFront.computeFacilityLocationCost(fgraph, isfcltOpen, witnesses);
				result.get(i)[neighborsCount - settings.neighborCountBeg] = cost;
			}

			expRunStatusHandler.stepsProcessed(
					counterPtr[0],
					experimentLength
			);
			counterPtr[0]++;
		}
		return result;
	}
}
